3 use Wikimedia\Services\ServiceContainer
;
6 * @covers Wikimedia\Services\ServiceContainer
8 class ServiceContainerTest
extends PHPUnit\Framework\TestCase
{
10 use MediaWikiCoversValidator
; // TODO this library is supposed to be independent of MediaWiki
11 use PHPUnit4And6Compat
;
13 private function newServiceContainer( $extraArgs = [] ) {
14 return new ServiceContainer( $extraArgs );
17 public function testGetServiceNames() {
18 $services = $this->newServiceContainer();
19 $names = $services->getServiceNames();
21 $this->assertInternalType( 'array', $names );
22 $this->assertEmpty( $names );
24 $name = 'TestService92834576';
25 $services->defineService( $name, function () {
29 $names = $services->getServiceNames();
30 $this->assertContains( $name, $names );
33 public function testHasService() {
34 $services = $this->newServiceContainer();
36 $name = 'TestService92834576';
37 $this->assertFalse( $services->hasService( $name ) );
39 $services->defineService( $name, function () {
43 $this->assertTrue( $services->hasService( $name ) );
46 public function testGetService() {
47 $services = $this->newServiceContainer( [ 'Foo' ] );
49 $theService = new stdClass();
50 $name = 'TestService92834576';
53 $services->defineService(
55 function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
57 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
58 PHPUnit_Framework_Assert
::assertSame( $extra, 'Foo' );
63 $this->assertSame( $theService, $services->getService( $name ) );
65 $services->getService( $name );
66 $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
69 public function testGetService_fail_unknown() {
70 $services = $this->newServiceContainer();
72 $name = 'TestService92834576';
74 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
76 $services->getService( $name );
79 public function testPeekService() {
80 $services = $this->newServiceContainer();
82 $services->defineService(
85 return new stdClass();
89 $services->defineService(
92 return new stdClass();
96 // trigger instantiation of Foo
97 $services->getService( 'Foo' );
99 $this->assertInternalType(
101 $services->peekService( 'Foo' ),
102 'Peek should return the service object if it had been accessed before.'
106 $services->peekService( 'Bar' ),
107 'Peek should return null if the service was never accessed.'
111 public function testPeekService_fail_unknown() {
112 $services = $this->newServiceContainer();
114 $name = 'TestService92834576';
116 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
118 $services->peekService( $name );
121 public function testDefineService() {
122 $services = $this->newServiceContainer();
124 $theService = new stdClass();
125 $name = 'TestService92834576';
127 $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
128 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
132 $this->assertTrue( $services->hasService( $name ) );
133 $this->assertSame( $theService, $services->getService( $name ) );
136 public function testDefineService_fail_duplicate() {
137 $services = $this->newServiceContainer();
139 $theService = new stdClass();
140 $name = 'TestService92834576';
142 $services->defineService( $name, function () use ( $theService ) {
146 $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException
::class );
148 $services->defineService( $name, function () use ( $theService ) {
153 public function testApplyWiring() {
154 $services = $this->newServiceContainer();
157 'Foo' => function () {
160 'Bar' => function () {
165 $services->applyWiring( $wiring );
167 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
168 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
171 public function testImportWiring() {
172 $services = $this->newServiceContainer();
175 'Foo' => function () {
178 'Bar' => function () {
181 'Car' => function () {
186 $services->applyWiring( $wiring );
188 $services->addServiceManipulator( 'Foo', function ( $service ) {
189 return $service . '+X';
192 $services->addServiceManipulator( 'Car', function ( $service ) {
193 return $service . '+X';
196 $newServices = $this->newServiceContainer();
198 // create a service with manipulator
199 $newServices->defineService( 'Foo', function () {
203 $newServices->addServiceManipulator( 'Foo', function ( $service ) {
204 return $service . '+Y';
207 // create a service before importing, so we can later check that
208 // existing service instances survive importWiring()
209 $newServices->defineService( 'Car', function () {
213 // force instantiation
214 $newServices->getService( 'Car' );
216 // Define another service, so we can later check that extra wiring
218 $newServices->defineService( 'Xar', function () {
222 // import wiring, but skip `Bar`
223 $newServices->importWiring( $services, [ 'Bar' ] );
225 $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
226 $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
228 // import all wiring, but preserve existing service instance
229 $newServices->importWiring( $services );
231 $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
232 $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
233 $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
234 $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
237 public function testLoadWiringFiles() {
238 $services = $this->newServiceContainer();
241 __DIR__
. '/TestWiring1.php',
242 __DIR__
. '/TestWiring2.php',
245 $services->loadWiringFiles( $wiringFiles );
247 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
248 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
251 public function testLoadWiringFiles_fail_duplicate() {
252 $services = $this->newServiceContainer();
255 __DIR__
. '/TestWiring1.php',
256 __DIR__
. '/./TestWiring1.php',
259 // loading the same file twice should fail, because
260 $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException
::class );
262 $services->loadWiringFiles( $wiringFiles );
265 public function testRedefineService() {
266 $services = $this->newServiceContainer( [ 'Foo' ] );
268 $theService1 = new stdClass();
269 $name = 'TestService92834576';
271 $services->defineService( $name, function () {
272 PHPUnit_Framework_Assert
::fail(
273 'The original instantiator function should not get called'
277 // redefine before instantiation
278 $services->redefineService(
280 function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
281 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
282 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
287 // force instantiation, check result
288 $this->assertSame( $theService1, $services->getService( $name ) );
291 public function testRedefineService_disabled() {
292 $services = $this->newServiceContainer( [ 'Foo' ] );
294 $theService1 = new stdClass();
295 $name = 'TestService92834576';
297 $services->defineService( $name, function () {
301 // disable the service. we should be able to redefine it anyway.
302 $services->disableService( $name );
304 $services->redefineService( $name, function () use ( $theService1 ) {
308 // force instantiation, check result
309 $this->assertSame( $theService1, $services->getService( $name ) );
312 public function testRedefineService_fail_undefined() {
313 $services = $this->newServiceContainer();
315 $theService = new stdClass();
316 $name = 'TestService92834576';
318 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
320 $services->redefineService( $name, function () use ( $theService ) {
325 public function testRedefineService_fail_in_use() {
326 $services = $this->newServiceContainer( [ 'Foo' ] );
328 $theService = new stdClass();
329 $name = 'TestService92834576';
331 $services->defineService( $name, function () {
335 // create the service, so it can no longer be redefined
336 $services->getService( $name );
338 $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException
::class );
340 $services->redefineService( $name, function () use ( $theService ) {
345 public function testAddServiceManipulator() {
346 $services = $this->newServiceContainer( [ 'Foo' ] );
348 $theService1 = new stdClass();
349 $theService2 = new stdClass();
350 $name = 'TestService92834576';
352 $services->defineService(
354 function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
355 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
356 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
361 $services->addServiceManipulator(
364 $theService, $actualLocator, $extra
366 $services, $theService1, $theService2
368 PHPUnit_Framework_Assert
::assertSame( $theService1, $theService );
369 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
370 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
375 // force instantiation, check result
376 $this->assertSame( $theService2, $services->getService( $name ) );
379 public function testAddServiceManipulator_fail_undefined() {
380 $services = $this->newServiceContainer();
382 $theService = new stdClass();
383 $name = 'TestService92834576';
385 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
387 $services->addServiceManipulator( $name, function () use ( $theService ) {
392 public function testAddServiceManipulator_fail_in_use() {
393 $services = $this->newServiceContainer( [ 'Foo' ] );
395 $theService = new stdClass();
396 $name = 'TestService92834576';
398 $services->defineService( $name, function () use ( $theService ) {
402 // create the service, so it can no longer be redefined
403 $services->getService( $name );
405 $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException
::class );
407 $services->addServiceManipulator( $name, function () {
412 public function testDisableService() {
413 $services = $this->newServiceContainer( [ 'Foo' ] );
415 $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService
::class )
417 $destructible->expects( $this->once() )
418 ->method( 'destroy' );
420 $services->defineService( 'Foo', function () use ( $destructible ) {
421 return $destructible;
423 $services->defineService( 'Bar', function () {
424 return new stdClass();
426 $services->defineService( 'Qux', function () {
427 return new stdClass();
430 // instantiate Foo and Bar services
431 $services->getService( 'Foo' );
432 $services->getService( 'Bar' );
434 // disable service, should call destroy() once.
435 $services->disableService( 'Foo' );
437 // disabled service should still be listed
438 $this->assertContains( 'Foo', $services->getServiceNames() );
440 // getting other services should still work
441 $services->getService( 'Bar' );
443 // disable non-destructible service, and not-yet-instantiated service
444 $services->disableService( 'Bar' );
445 $services->disableService( 'Qux' );
447 $this->assertNull( $services->peekService( 'Bar' ) );
448 $this->assertNull( $services->peekService( 'Qux' ) );
450 // disabled service should still be listed
451 $this->assertContains( 'Bar', $services->getServiceNames() );
452 $this->assertContains( 'Qux', $services->getServiceNames() );
454 $this->setExpectedException( Wikimedia\Services\ServiceDisabledException
::class );
455 $services->getService( 'Qux' );
458 public function testDisableService_fail_undefined() {
459 $services = $this->newServiceContainer();
461 $theService = new stdClass();
462 $name = 'TestService92834576';
464 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
466 $services->redefineService( $name, function () use ( $theService ) {
471 public function testDestroy() {
472 $services = $this->newServiceContainer();
474 $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService
::class )
476 $destructible->expects( $this->once() )
477 ->method( 'destroy' );
479 $services->defineService( 'Foo', function () use ( $destructible ) {
480 return $destructible;
483 $services->defineService( 'Bar', function () {
484 return new stdClass();
487 // create the service
488 $services->getService( 'Foo' );
490 // destroy the container
491 $services->destroy();
493 $this->setExpectedException( Wikimedia\Services\ContainerDisabledException
::class );
494 $services->getService( 'Bar' );